package com.scilvet.reportcomposer.account;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;

public class AccountID implements Comparable<AccountID>, Iterable<String> {
	
	private static final int componentNumber = 3;
	private static final int[] idComponentLengthArray = { 2, 2, 3 };
	
	public static final String COMPONENT_SEPARATOR = "/";
	public static final String WILDCARD = "*";
	public static final AccountID ALL_ACCOUNTS = new AccountID(WILDCARD, WILDCARD, WILDCARD);
	
	protected List<String> idComponentList;
	
	public AccountID(String account) {
		
		if (account == null) {
			throw new IllegalArgumentException("Cannot create AccountID from null string");
		}
		
		int stringLength = componentNumber - 1;
		for (int idComponentLength : idComponentLengthArray) {
			stringLength += idComponentLength;
		}
		
		//TODO: refactor this shit
		// remove all the / and then substring using the known component lengths
		// 14/00000 workaround
		if (account.length() == stringLength - 1) {
			account = account.replace("00000", "00/000");
		}
		
		if (account.length() != stringLength) {
			throw new IllegalArgumentException("AccountID init string must be " + stringLength + " characters long");
		}
		
		String[] idComponentArray = StringUtils.split(account, COMPONENT_SEPARATOR);
		
		if (idComponentArray.length != componentNumber) {
			throw new IllegalArgumentException("AccountID can contain only " + componentNumber + " components");
		}
		
		this.idComponentList = correctIDComponentLengths(Arrays.asList(idComponentArray));
	}
	
	public AccountID(String first, String second, String third) {

		List<String> idComponentList = new ArrayList<String>(componentNumber);
		idComponentList.add(first);
		idComponentList.add(second);
		idComponentList.add(third);
		
		this.idComponentList = correctIDComponentLengths(idComponentList);
	}
	
	public AccountID(List<String> idComponentList) {
		
		if (idComponentList.size() != componentNumber) {
			throw new IllegalArgumentException("AccountID can contain only " + componentNumber + " components");
		}
		
		this.idComponentList = correctIDComponentLengths(idComponentList);
	}
	
	private List<String> correctIDComponentLengths(List<String> idComponentList) {
		List<String> correctIDComponentList = new ArrayList<String>(componentNumber);
		String idComponent;
		
		for (int i = 0; i < componentNumber; i++) {
			
			idComponent = idComponentList.get(i);
			if (idComponent.length() > idComponentLengthArray[i]) {
				throw new IllegalArgumentException("AccountID Component " + i +
						"must be at most" + idComponentLengthArray[i] + " characters long");
			}
			
			if (AccountID.isWildcard(idComponent)) {
				idComponent = StringUtils.repeat(WILDCARD, idComponentLengthArray[i]);
			} else {
				idComponent = StringUtils.leftPad(idComponent, idComponentLengthArray[i], '0');
			}
			correctIDComponentList.add(i, idComponent);
		}
		
		return correctIDComponentList;
	}
	
	public String getFirst() {
		return idComponentList.get(0);
	}
	
	public String getSecond() {
		return idComponentList.get(1);
	}
	
	public String getThird() {
		return idComponentList.get(2);
	}
	
	protected AccountID getWildcard(int level) {
		
		if (level < 0 || level > componentNumber) {
			throw new IllegalArgumentException("Max depth for ID " + this.toString() +
					" is " + componentNumber);
		}
		
		List<String> wildcardIdComponentList = new ArrayList<String>(level);
		for (int i = 0; i < idComponentList.size(); i++) {
			if (i < level) {
				wildcardIdComponentList.add(idComponentList.get(i));
			} else {
				wildcardIdComponentList.add(WILDCARD);
			}
		}
		
		return new AccountID(wildcardIdComponentList);
	}
	
	public String getComponent(int level) {
		
		if (level < 0 || level >= componentNumber) {
			throw new IllegalArgumentException("Max depth for ID " + this.toString() +
					" is " + componentNumber);
		}
		
		return idComponentList.get(level);
	}
	
	public boolean isWildcard(int level) {
		return isWildcard(idComponentList.get(level));
	}
	
	public static boolean isWildcard(String idComponent) {
		
		if (idComponent.matches("\\*+")) {
			return true;
		}
		
		return false;
	}
	
	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		
		for(int i = 0; i < componentNumber; i++) {
			
			builder.append(idComponentList.get(i));
			
			if (i != componentNumber - 1) {
				builder.append(COMPONENT_SEPARATOR);
			}
		}
		
		return builder.toString();
	}
	
	@Override
	public Iterator<String> iterator() {
		return idComponentList.iterator();
	}
	
	@Override
	public int compareTo(AccountID other) {
		
		int comparison = 0;
		
		for (int i = 0; i < componentNumber; i++) {
			comparison = idComponentList.get(i).compareTo(other.getComponent(i));
			if (comparison != 0) {
				return comparison;
			}
		}
		
		return comparison;
	}

}
